package com.hamilton.erik.pt.mobile.util;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import android.util.Log;

import com.hamilton.erik.pt.core.enumeration.EnclosureMaintenanceAction;
import com.hamilton.erik.pt.core.enumeration.Gender;
import com.hamilton.erik.pt.core.enumeration.MedicalAction;
import com.hamilton.erik.pt.core.model.Animal;
import com.hamilton.erik.pt.core.model.Enclosure;
import com.hamilton.erik.pt.core.model.EnclosureMaintenance;
import com.hamilton.erik.pt.core.model.Feeding;
import com.hamilton.erik.pt.core.model.MedicalRecord;
import com.hamilton.erik.pt.core.model.Note;

public class JsonUtil {
	private static JsonUtil instance = null;
	
	public static JsonUtil getInstance() {
		if(instance == null) {
			instance = new JsonUtil();
		}
		
		return instance;
	}

	private JsonUtil() {}
	
	public List getList(JSONObject jsonObject, String key) throws JSONException {
		return asList(jsonObject.getJSONArray(key));
	}
	public Map<String, Object> getMap(JSONObject object, String key) throws JSONException {
		return asMap(object.getJSONObject(key));
	}

	public Map<String, Object> asMap(JSONObject jsonObject)
			throws JSONException {
		Map<String, Object> map = new HashMap<String, Object>();
		Iterator keys = jsonObject.keys();
		while (keys.hasNext()) {
			String key = (String) keys.next();
			map.put(key, fromJson(jsonObject.get(key)));
		}
		return map;
	}

	public List asList(JSONArray jsonArray) throws JSONException {
		List list = new ArrayList();
		for(int i = 0 ; i < jsonArray.length(); i++) {
			list.add(fromJson(jsonArray.get(i)));
		}
		
		return list;
	}

	private Object fromJson(Object json) throws JSONException {
		if (json == JSONObject.NULL) {
			return null;
		} else if (json instanceof JSONObject) {
			return asMap((JSONObject)json);
		} else if (json instanceof JSONArray) {
			return asList((JSONArray)json);
		} else {
			return json;
		}
	}
	
	public Animal getAnimal(JSONObject json) throws JSONException {
		Animal animal = new Animal();
		
		if(!isMissingOrNull(json, "id")) {
			animal.setId(json.getLong("id"));
		}
		
		if(!isMissingOrNull(json, "userName")) {
			animal.setUserName(json.getString("userName"));
		}
		
		if(!isMissingOrNull(json, "type")) {
			animal.setType(json.getString("type"));
		}
		
		if(!isMissingOrNull(json, "genus")) {
			animal.setGenus(json.getString("genus"));
		}
		
		if(!isMissingOrNull(json, "species")) {
			animal.setSpecies(json.getString("species"));
		}
		
		if(!isMissingOrNull(json, "name")) {
			animal.setName(json.getString("name"));
		}
		
		if(!isMissingOrNull(json, "birthDate")) {
			animal.setBirthDate(json.getString("birthDate"));
		}
		
		if(!isMissingOrNull(json, "purchaseDate")) {
			animal.setPurchaseDate(json.getString("purchaseDate"));
		}
		
		if(!isMissingOrNull(json, "gender")) {
			animal.setGender(json.getString("gender"));
		}
		
		return animal;
	}
	
	public Feeding getFeeding(JSONObject json) throws JSONException {
		Feeding feeding = new Feeding();
		
		if(!isMissingOrNull(json, "id")) {
			feeding.setId(json.getLong("id"));
		}
		
		if(!isMissingOrNull(json, "animalId")) {
			feeding.setAnimalId(json.getLong("animalId"));
		}
		
		if(!isMissingOrNull(json, "feedingItem")) {
			feeding.setFeedingItem(json.getString("feedingItem"));
		}
		
		if(!isMissingOrNull(json, "measurement")) {
			feeding.setMeasurement(json.getDouble("measurement"));
		}
		
		if(!isMissingOrNull(json, "measurementUnit")) {
			feeding.setMeasurementUnit(json.getString("measurementUnit"));
		}
		
		if(!isMissingOrNull(json, "timestamp")) {
			feeding.setTimestamp(json.getString("timestamp"));
		}
		
		return feeding;
	}
	
	public Enclosure getEnclosure(JSONObject json) throws JSONException {
		Enclosure enclosure = new Enclosure();
		
		if(!isMissingOrNull(json, "id")) {
			enclosure.setId(json.getLong("id"));
		}
		
		if(!isMissingOrNull(json, "animalId")) {
			enclosure.setAnimalId(json.getLong("animalId"));
		}
		
		if(!isMissingOrNull(json, "enclosureType")) {
			enclosure.setEnclosureType(json.getString("enclosureType"));
		}
		
		if(!isMissingOrNull(json, "shape")) {
			enclosure.setShape(json.getString("shape"));
		}
		
		if(!isMissingOrNull(json, "height")) {
			enclosure.setHeight(json.getDouble("height"));
		}
		
		if(!isMissingOrNull(json, "length")) {
			enclosure.setLength(json.getDouble("length"));
		}
		
		if(!isMissingOrNull(json, "width")) {
			enclosure.setWidth(json.getDouble("width"));
		}
		
		if(!isMissingOrNull(json, "volume")) {
			enclosure.setVolume(json.getDouble("volume"));
		}
		
		if(!isMissingOrNull(json, "volumeUnit")) {
			enclosure.setVolumeUnit(json.getString("volumeUnit"));
		}
		
		return enclosure;
	}
	
	public EnclosureMaintenance getEnclosureMaintenance(JSONObject json) throws JSONException {
		EnclosureMaintenance enclosureMaintenance = new EnclosureMaintenance();
		
		if(!isMissingOrNull(json, "id")) {
			enclosureMaintenance.setId(json.getLong("id"));
		}
		
		if(!isMissingOrNull(json, "enclosureId")) {
			enclosureMaintenance.setEnclosureId(json.getLong("enclosureId"));
		}
		
		if(!isMissingOrNull(json, "action")) {
			enclosureMaintenance.setAction(json.getString("action"));
		}
		
		if(!isMissingOrNull(json, "timestamp")) {
			enclosureMaintenance.setTimestamp(json.getString("timestamp"));
		}
		
		return enclosureMaintenance;
	}
	
	public MedicalRecord getMedicalRecord(JSONObject json) throws JSONException {
		MedicalRecord medicalRecord = new MedicalRecord();
		
		if(!isMissingOrNull(json, "id")) {
			medicalRecord.setId(json.getLong("id"));
		}
		
		if(!isMissingOrNull(json, "animalId")) {
			medicalRecord.setAnimalId(json.getLong("animalId"));
		}
		
		if(!isMissingOrNull(json, "action")) {
			medicalRecord.setAction(json.getString("action"));
		}
		
		if(!isMissingOrNull(json, "timestamp")) {
			medicalRecord.setTimestamp(json.getString("timestamp"));
		}
		
		return medicalRecord;
	}
	
	public Note getNote(JSONObject json) throws JSONException {
		Note note = new Note();
		
		if(!isMissingOrNull(json, "id")) {
			note.setId(json.getLong("id"));
		}
		
		if(!isMissingOrNull(json, "referenceId")) {
			note.setReferenceId(json.getLong("referenceId"));
		}
		
		if(!isMissingOrNull(json, "title")) {
			note.setTitle(json.getString("title"));
		}
		
		if(!isMissingOrNull(json, "note")) {
			note.setNote(json.getString("note"));
		}
		
		if(!isMissingOrNull(json, "date")) {
			note.setDate(json.getString("date"));
		}
		
		return note;
	}
	
	private String safeGetString(JSONObject json, String key) {
		String result = "";
		try {
			result = json.getString(key);
		} catch (Exception e) {
			Log.w("JsonUtil",
					String.format("Failed to retrieve string value from JSON [%s] " +
								  "with key [%s]", json.toString(), key));
		}
		
		if(result == null) {
			result = ""; 
		}
		
		return result;
	}
	
	private double safeGetDouble(JSONObject json, String key) {
		double result = -1;

		try {
			result = json.getDouble(key);
		} catch (Exception e) {
			Log.w("JsonUtil",
					String.format("Failed to retrieve double value from JSON [%s] " +
								  "with key [%s]", json.toString(), key));
		}
		
		return result;
	}
	
	private long safeGetLong(JSONObject json, String key) {
		long result = -1;

		try {
			result = json.getLong(key);
		} catch (Exception e) {
			Log.w("JsonUtil",
					String.format("Failed to retrieve long value from JSON [%s] " +
								  "with key [%s]", json.toString(), key));
		}
		
		return result;
	}
	
	public boolean isMissingOrNull(JSONObject json, String key) throws JSONException {
		 return (!json.has(key) || (json.get(key) == JSONObject.NULL));
	}
}
